The development of the EDVAC computer system of 1948 is often cited as the beginning of the computer era. Since that time, computer systems have evolved into extremely sophisticated devices, and computer systems may be found in many different settings. Computer systems typically include a combination of hardware (such as semiconductors, integrated circuits, programmable logic devices, programmable gate arrays, and circuit boards) and software, also known as computer programs. As advances in semiconductor processing and computer architecture push the performance of the computer hardware higher, more sophisticated and complex computer software has evolved to take advantage of the higher performance of the hardware, resulting in computer systems today that are much more powerful than just a few years ago.
As the sophistication and complexity of computer software increase, the more difficult the software is to debug. Bugs are problems, faults, or errors in a computer program. Locating, analyzing, and correcting suspected faults in a computer program is a process known as “debugging.” Typically, a programmer uses another computer program commonly known as a “debugger” to debug a program under development.
Conventional debuggers typically support two primary operations to assist a computer programmer. A first operation supported by conventional debuggers is a “step” function, which permits a computer programmer to process instructions (also known as “statements”) in a computer program one-by-one and see the results upon completion of each instruction. While the step operation provides a programmer with a large amount of information about a program during its execution, stepping through hundreds or thousands of program instructions can be extremely tedious and time consuming and may require a programmer to step through many program instructions that are known to be error-free before a set of instructions to be analyzed are executed.
To address this difficulty, a second operation supported by conventional debuggers is a breakpoint operation, which permits a computer programmer to identify with a breakpoint a precise instruction for which it is desired to halt execution of a computer program during execution. As a result, when a computer program is executed by a debugger, the program executes in a normal fashion until a breakpoint is reached. The debugger then stops execution of the program and displays the results of the program to the programmer for analysis.
Typically, step operations and breakpoints are used together to simplify the debugging process. Specifically, a common debugging operation is to set a breakpoint at the beginning of a desired set of instructions to be analyzed and then begin executing the program. Once the breakpoint is reached, the debugger halts the program, and the programmer then steps through the desired set of instructions line-by-line using the step operation. Consequently, a programmer is able to more quickly isolate and analyze a particular set of instructions without needing to step through irrelevant portions of a computer program.
Computer programs being debugged are either compiled for execution by a compiler or executed by an interpreter. One example of an interpreter is the Java Virtual Machine (JVM), which employs a class loader to load classes used by the program being debugged on an as-needed basis. The classpath tells the class loader where to find third-party and user-defined classes. Classpath entries may be directories that contain classes not in a package, the package root directory for classes in a package, or archive files (e.g. .zip or jar files) that contain classes. The class loader loads classes in the order they appear in the classpath. For example, starting with the first classpath entry, the class loader visits each specified directory or archive file attempting to find the class to load. The first class found with the proper name is loaded, and any remaining classpath entries are ignored.
The classpath can become a source of great frustration and annoyance for the user because as the number of dependent third-party and user-defined classes increases for the program being debugged, the classpath becomes a dumping ground for every conceivable directory and archive file, and the risk becomes greater that the class contains duplicate class entries. Thus, the user can experience great difficulty in determining which class the class loader will load first. For example, the user may append a directory to the classpath in attempt to get the latest version of a class loaded into the program being debugged, but the user may be unaware that another version of the class is located in a directory of higher precedence in the classpath.
Without a better way to handle classpaths, the debugging process will continue to be a difficult and time-consuming task, which delays the introduction of software products and increases their costs. Although the aforementioned problems have been described in the context of the Java class loader and programs under debug, they can occur in any compiler or interpreter, in any type of computer language, and in non-debug environments as well as in debug environments.